<?php

include "ns1.php";
include "ns2.php";
include "fn/ns11.php";  //其它文件夹

use function ns1\test_ns1;
use function ns2\test_ns2;
//use \fn\ns11;

use ns1 as N1;  //别名

echo '----------------------- ns.php begin --------------------'.PHP_EOL;

echo 'namespced desc:', __NAMESPACE__, PHP_EOL;


function test() {
    echo "ns test".PHP_EOL;
}


class Tc {
    function __construct($p) {
        echo "::Tc constructor:".$p.PHP_EOL;
        $this->p = $p;
    }
    public function test() {
        echo "Tc test:".$this->p.PHP_EOL;
    }
    function test_public() {
        echo "Tc test_public:".$this->p.PHP_EOL;
    }
    protected function test_protected() {
        echo "Tc test_protected:".$this->p.PHP_EOL;
    }
    private function test_private() {
        echo "Tc test_private:".$this->p.PHP_EOL;
    }

    final function test_final() { //子类无法重写
        echo "Tc test_final:".$this->p.PHP_EOL;
    }

    function test_ppp() {
        echo "------------ Tc test_ppp:".$this->p.PHP_EOL;
        $this->test_public();
        $this->test_protected();
        $this->test_private();
    }

    public $p;
    protected $p1;
    private $p2 = 43;
}

class Tc1 extends Tc {
    // 可以对 public 和 protected 进行重定义，但 private 而不能
    protected $p1 = 222;
    private $p2 = 111;

    /*
    如果子类没有重写构造函数将会调用父类的构造函数。重写了则不会自动调用，需要显示调用父类构造函数
    */
    function __construct($p) {
        // 非自动 需要显示调用
        parent::__construct($p);
        echo "::Tc1 constructor:".$p.PHP_EOL;
    }

    public function show() {
        echo "Tc1 info:".$this->p, ', ', $this->p1, ', ', $this->p2.PHP_EOL;
    }
    function show2() {
        echo "Tc1 info 2:".$this->p, ', ', $this->p1, ', ', $this->p2.PHP_EOL;
    }

    /* final function test_final() { //子类无法重写
        echo "Tc test_final:".$this->p.PHP_EOL;
    } */


    /*
    重写父类的方法:父类私有的属性和方法不能让子类直接调用，并不是说没有继承过来，而是继承过来了，但是要调用父类的私有属性和方法的
    话还是需要通过父类的 public、protected 方法调用)
    */
    function test_public() {
        echo "Tc1 test_public:".$this->p.PHP_EOL;
    }
    protected function test_protected() {
        echo "Tc1 test_protected:".$this->p.PHP_EOL;
    }
    private function test_private() {
        echo "Tc1 test_private:".$this->p.PHP_EOL;
    }

}

echo '命名空间接口调用:'.PHP_EOL;

echo 'test():', test();
echo '\test():', \test();

ns1\test();
ns2\test();

test_ns1();
test_ns2();
echo 'N1\test():', N1\test(), PHP_EOL;
\ns11\test();   //调用其它目录的

echo '------------------------------ 类测试:'.PHP_EOL;

$tc = new \Tc(1); //ns1
$tc->test();

$s = 'Tc';
$tc1 = new $s(21);
$tc1->test();

$stc = new Tc1(2211);
echo 'stc->p:', $stc->p.PHP_EOL;
$stc->show();
$stc->show2();
//无法访问保护/私有
//echo $stc->p1.PHP_EOL;
//echo $stc->p2.PHP_EOL;
//$stc->test_protected();
//$stc->test_private();

//可以访问子类公共/受保护 但是私有只能是基类
$stc->test_ppp();


echo '命名空间和动态语言特征:'.PHP_EOL;

$fs = 'ns1\test';
$fs();

const ns1_name = 'ns1';
echo 'ns1_name:' . constant('ns1_name') . PHP_EOL;

const ns1_tc_name = 'ns1\test';
echo constant('ns1_tc_name').PHP_EOL;
echo constant('\ns1_name').PHP_EOL;
echo constant('ns1\a1').PHP_EOL;


echo '类接口:'.PHP_EOL;

interface ITest {
    public function test();
}


class Test implements ITest {
    public function test() { //实现
        echo "Test test".PHP_EOL;
    }
    function show_a() {
        echo "Test show_a:", self::a, PHP_EOL; //伪变量$this在常量/静态方法中不可用 用self修饰
    }
    const a = 4467;
}

$t = new Test();
$t->test();

//可以用一个变量来动态调用类
$vt = 'Test';
echo 'vt test::a:', $vt::a.PHP_EOL;

echo 'Test::a:', Test::a.PHP_EOL;
$t->show_a();


echo '类的抽象:'.PHP_EOL;
abstract class Ab {
    abstract function test_a();
    abstract function test_b($p);

    function test() {
        $this->test_a().PHP_EOL;
    }
}

class Ab1 extends Ab {
    function test_a() {
        echo "Ab1 test_a".PHP_EOL;
    }

    //子类方法可以包含父类抽象方法中不存在的可选参数
    function test_b($p1, $p2 = 0) {
        echo "Ab1 test_b:".$p1.', '.$p2.PHP_EOL;
    }
}

$ab = new Ab1();
$ab->test();
$ab->test_b(123);
$ab->test_b(123, 456);



echo '----------------------- ns.php end --------------------'.PHP_EOL;
?>


<!-- https://www.runoob.com/php/php-oop.html -->
 